package com.idea.plugin.core.resolver;

import com.idea.plugin.core.constant.InitConstant;
import com.idea.plugin.core.domain.dto.JavaMethodDTO;
import com.idea.plugin.core.domain.dto.JavaMethodExceptionsDTO;
import com.idea.plugin.core.domain.info.JavaClassInfo;
import com.idea.plugin.core.helper.JavaClassHelper;
import com.idea.plugin.core.logging.Log;
import com.idea.plugin.core.logging.SystemStreamLog;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiMethod;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 单元测试方法
 */
public class UnitTestingMethodResolver {

    private static Log log = new SystemStreamLog();

    /**
     * 构建测试类核心方法
     *
     * @param javaClassInfo 贯穿本次类构造，记录类信息
     * @return 方法DTO集合
     */
    public static List<JavaMethodDTO> build(JavaClassInfo javaClassInfo) {

        PsiClass javaClass = javaClassInfo.getPsiClass();

        List<JavaMethodDTO> javaMethodDTOList = new ArrayList<>();

        Map<String, Integer> methodMap = javaClassInfo.getMethodMap();

        //获取方法集合
//        List<JavaMethod> javaMethodList = javaClass.getMethods();
        PsiMethod[] javaMethodList = javaClass.getMethods();
        //遍历类中的方法
        for (PsiMethod javaMethod : javaMethodList) {

            //排除静态方法和私有方法
            if (excludeMethod(javaMethod)) {
                continue;
            }

            JavaMethodDTO javaMethodDTO = new JavaMethodDTO();

            //获取方法名称
            String methodName = javaMethod.getName();
            javaMethodDTO.setMethodName(methodName);

            //处理重名方法
            methodDdealingWithRenaming(methodMap, methodName, javaMethodDTO);

            //获取方法返回类型
            String returnValueStr = JavaClassHelper.getReturnTypeQualifiedName(javaMethod);
            javaMethodDTO.setReturnFullyType(returnValueStr);
            returnValueStr = InitConstant.getAbbreviation(returnValueStr);
            javaMethodDTO.setReturnType(returnValueStr);

            //方法参数的设置，包装类设置属性 默认值
            UnitTestingMethodParameterResolver.build(javaMethod, javaClassInfo, javaMethodDTO, javaClass);

            //方法抛出的异常
            List<JavaMethodExceptionsDTO> javaMethodExceptionsDTOS = getJavaExceptionsDTOList(javaMethod);
            javaMethodDTO.setJavaMethodExceptionsDTOList(javaMethodExceptionsDTOS);

            //stub method
            StubMockMethodResolver.buildMock(javaClassInfo, javaMethod, javaMethodDTO);

            javaMethodDTOList.add(javaMethodDTO);
        }
        return javaMethodDTOList;
    }


    /**
     * 处理重名方法
     *
     * @param methodMap     测试方法名称出现的次数，如果有多个重名方法，方法后面接上数字
     * @param methodName    方法名称
     * @param javaMethodDTO 方法展示信息
     */
    private static void methodDdealingWithRenaming(Map<String, Integer> methodMap, String methodName, JavaMethodDTO javaMethodDTO) {
        javaMethodDTO.setMethodTestName(methodName + "Test");
        if (methodMap.containsKey(methodName)) {
            Integer t = methodMap.get(methodName);
            javaMethodDTO.setMethodTestName(javaMethodDTO.getMethodTestName() + t);
            methodMap.put(methodName, ++t);
        } else {
            methodMap.put(methodName, 1);
        }
    }


    /**
     * 排除静态方法和私有方法
     *
     * @param javaMethod 类方法信息
     * @return true-进行排除，false-不是静态方法和私有方法
     */
    private static boolean excludeMethod(PsiMethod javaMethod) {
        //是否是静态方法
        if (JavaClassHelper.isStatic(javaMethod)) {
            return true;
        }
        return JavaClassHelper.isPrivate(javaMethod);
    }


    /**
     * 方法抛出的异常
     *
     * @param javaMethod
     * @return
     */
    private static List<JavaMethodExceptionsDTO> getJavaExceptionsDTOList(PsiMethod javaMethod) {
//        psiClass.getMethods()[3].getThrowsList().getReferenceElements()[0].getQualifiedName()
        PsiJavaCodeReferenceElement[] exceptions = JavaClassHelper.getMethodThrowsException(javaMethod);
//        List<JavaClass> exceptions = javaMethod.getExceptions();
        List<JavaMethodExceptionsDTO> javaMethodExceptionsDTOS = new ArrayList<>();
        for (PsiJavaCodeReferenceElement exception : exceptions) {
            JavaMethodExceptionsDTO javaMethodExceptionsDTO = new JavaMethodExceptionsDTO();
            javaMethodExceptionsDTO.setType(exception.getQualifiedName());
            javaMethodExceptionsDTOS.add(javaMethodExceptionsDTO);
        }
        return javaMethodExceptionsDTOS;
    }


}
